﻿using Commons.Helpers;
using Commons.Responses;
using DomainCommons.Models;
using IRepositories;
using Microsoft.EntityFrameworkCore;
using ORM;
using Repositories.Helpers;
using System.Linq.Expressions;

namespace Repositories
{
    /// <summary>
    /// 针对仓储泛型接口 <see cref="IRepository{TEntity}" /> 的具体实现。
    /// </summary>
    /// <typeparam name="TEntity"></typeparam>
    public class BaseRepository<T, TEntity> : IBaseRepository<T, TEntity> where TEntity : class, IEntity<T>,  ISoftDelete,IHasCreationTime, new()
    {
        /// <summary>
        /// EF 中实现领域模型对象和关系数据库表对象映射关联上下文对象
        /// </summary>
        private readonly PSPDbContext _context;
        internal PSPDbContext NewsContext { get => _context; }
        public PSPDbContext Context { get => _context; }
        public BaseRepository(PSPDbContext context)
        {
            _context = context;
        }



        public async Task<bool> HasAsync(T id) => await _context.Set<TEntity>()!.AnyAsync(x => x.Id.Equals(id));
        public async Task<bool> AnyAsync(Expression<Func<TEntity, bool>> predicate)
        {
           return await _context.Set<TEntity>()!.AnyAsync(predicate);
        }

        public async Task<DatasResponse<TEntity>> GetAllAsync()
        {
            IQueryable<TEntity> dbSet = _context!.Set<TEntity>();
            var includePropertyExpressionCollection = ModelRelation.GetIncludeExpression<T, TEntity>();
            if (includePropertyExpressionCollection != null)
            {
                foreach (var includePropertyExpression in includePropertyExpressionCollection)
                {
                    dbSet = dbSet.Include(includePropertyExpression);
                }
            }
            var data = await dbSet!.ToListAsync();
            var total = await dbSet.CountAsync();
            var result = new DatasResponse<TEntity>(total,data) ;
            return result!;
        }

        public virtual async Task<DatasResponse<TEntity>> GetAllAsync(Expression<Func<TEntity, bool>> predicate,  Dictionary<string, bool> orderByFields = null)
        {
            IQueryable<TEntity> dbSet = _context!.Set<TEntity>();
            var includePropertyExpressionCollection = ModelRelation.GetIncludeExpression<T, TEntity>();
            if (includePropertyExpressionCollection != null)
            {
                foreach (var includePropertyExpression in includePropertyExpressionCollection)
                {
                    dbSet = dbSet.Include(includePropertyExpression);
                }
            }
            if(orderByFields == null)
            {
                dbSet = dbSet.OrderByDescending(x => x.CreationTime);
            }
            // 动态排序
            if (orderByFields != null && orderByFields.Any())
            {
                foreach (var orderByField in orderByFields)
                {
                    var propertyInfo = typeof(TEntity).GetProperty(orderByField.Key);
                    if (propertyInfo == null)
                    {
                        throw new ArgumentException($"属性 '{orderByField.Key}' 不存在于类型 '{typeof(TEntity).Name}' 中。");
                    }

                    if (orderByField.Value)
                    {
                        dbSet = dbSet.OrderByDescending(e => EF.Property<object>(e, orderByField.Key));
                    }
                    else
                    {
                        dbSet = dbSet.OrderBy(e => EF.Property<object>(e, orderByField.Key));
                    }
                }
            }
            if (predicate != null)
            {
                dbSet = dbSet.Where(predicate);
            }
            // 获取数据
            var data = await dbSet.ToListAsync();
            var total = await dbSet.CountAsync();
            var result = new DatasResponse<TEntity>(total, data);
            return result!;
        }

        public async Task<TEntity> GetAsync(T id)
        {
            IQueryable<TEntity> dbSet = _context!.Set<TEntity>();
            var includePropertyExpressionCollection = ModelRelation.GetIncludeExpression<T, TEntity>();
            if (includePropertyExpressionCollection != null)
            {
                foreach (var includePropertyExpression in includePropertyExpressionCollection)
                {
                    dbSet = dbSet.Include(includePropertyExpression);
                    
                }
            }
            var result = await dbSet!.FirstOrDefaultAsync(x => x.Id.Equals(id));
            return result!;
        }

        public async Task<TEntity> GetAsync(Expression<Func<TEntity, bool>> predicate)
        {
            IQueryable<TEntity> dbSet = _context!.Set<TEntity>();
            var includePropertyExpressionCollection = ModelRelation.GetIncludeExpression<T, TEntity>();
            if (includePropertyExpressionCollection != null)
            {
                foreach (var includePropertyExpression in includePropertyExpressionCollection)
                {
                    dbSet = dbSet.Include(includePropertyExpression);
                }
            }

            var result = await dbSet!.FirstOrDefaultAsync(predicate);
            return result!;
        }

        public async Task<DataResponse<Other>> GetOtherByIdAsync<Tid, Other>(Tid id) where Other : class, IEntity<Tid>, new()
        {
            IQueryable<Other> dbSet = _context!.Set<Other>();
            var includePropertyExpressionCollection = ModelRelation.GetIncludeExpression<Tid, Other>();
            if (includePropertyExpressionCollection != null)
            {
                foreach (var includePropertyExpression in includePropertyExpressionCollection)
                {
                    dbSet = dbSet.Include(includePropertyExpression);
                }
            }
            var result = await dbSet.FirstOrDefaultAsync(x => x.Id.Equals(id));
            return new DataResponse<Other>(result);
        }

        public virtual async  Task<DatasResponse<TEntity>> GetBySelectAsync(int pageIndex, int pageSize, Expression<Func<TEntity, bool>> predicate)
        {
            IQueryable<TEntity> dbSet = _context!.Set<TEntity>();
            var includePropertyExpressionCollection = ModelRelation.GetIncludeExpression<T, TEntity>();
            if (includePropertyExpressionCollection != null)
            {
                foreach (var includePropertyExpression in includePropertyExpressionCollection)
                {
                    dbSet = dbSet.Include(includePropertyExpression);
                }
            }
            if (pageIndex == 0)
            {
                pageIndex = 1;
            }
            if (predicate != null)
            {
                if (pageSize < 1)
                    pageSize = 100;
                var datas = await dbSet.Where(predicate).Skip((pageIndex - 1) * pageSize).Take(pageSize).ToListAsync();
                var total = await dbSet.Where(predicate).CountAsync();
                return new DatasResponse<TEntity>(total,datas);
            }
            else
            {
                if (pageSize < 1)
                    pageSize = 100;
                var datas = await dbSet.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToListAsync();
                var total = await dbSet.CountAsync();
                return new DatasResponse<TEntity>(total, datas);
            }
        }

        

        public virtual async Task<DataResponse<TEntity>> AddAsync(TEntity ddo)
        {
            try
            {
                _context.Set<TEntity>().Add(ddo);
                await _context.SaveChangesAsync();
                return new DataResponse<TEntity>(ddo,"添加成功!");
            }
            catch(Exception ex) 
            {
                throw new CustomException("添加失败!");
            }
        }

        public virtual async  Task<MessageResponse> UpdateAsync(TEntity ddo)
        {

            if (!await _context.Set<TEntity>().AnyAsync(x => x.Id.Equals(ddo.Id)))
                return new MessageResponse("没有这条Id的数据!",false);
            else
            {
                try
                {
                    if (_context.ChangeTracker.Entries<TEntity>().Any(x => x.Entity.Id.Equals(ddo.Id)))
                    {
                        var trackingBo = _context.ChangeTracker.Entries<TEntity>().AsQueryable().Where(x => x.Entity.Id.Equals(ddo.Id)).FirstOrDefault();
                        trackingBo.State = EntityState.Detached;
                    }
                    _context.Set<TEntity>().Update(ddo);
                    await _context.SaveChangesAsync();
                    return new MessageResponse("更新成功!");
                }
                catch(Exception e)
                {
                    throw new CustomException("更新失败!");
                }
            }
        }

        public async Task<MessageResponse> DeleteAsync(T id)
        {
            var bo = await _context!.Set<TEntity>().FirstOrDefaultAsync(x=>x.Id.Equals(id));
            if(bo == null)
            {
                throw new CustomException("不存在的数据!");
            }
            var trackingBo = _context.ChangeTracker.Entries<TEntity>().AsQueryable().Where(x => x.Entity.Id.Equals(bo.Id)).FirstOrDefault();
            trackingBo.State = EntityState.Deleted;
            try
            {
                _context!.Set<TEntity>().Remove(bo);
                await _context!.SaveChangesAsync();
                return new MessageResponse("删除成功!");
            }
            catch (Exception)
            {
                throw new CustomException("删除失败!");
            }


        }
        public async Task<MessageResponse> SoftDeleteAsync(T id)
        {
            var bo = await _context!.Set<TEntity>().FirstOrDefaultAsync(x => x.Id.Equals(id));
            if (bo == null)
            {
                throw new CustomException("不存在的数据!");
            }
            bo.SoftDeleted();
            try
            {
                _context!.Set<TEntity>().Update(bo);
                await _context!.SaveChangesAsync();
                return new MessageResponse("删除成功!");
            }
            catch 
            {
                throw new CustomException("删除失败!");
            }
        }

        public async Task<MessageResponse> DeleteRangeAsync(List<TEntity> bos)
        {
            try
            {
                _context!.Set<TEntity>().RemoveRange(bos);
                await _context.SaveChangesAsync();
                return new MessageResponse("删除成功!");
            }
            catch
            {
                throw new CustomException("删除失败!");
            }
        }

        public async Task<MessageResponse> DeleteRangeByIdAsync(List<T> ids)
        {
            var bos = new List<TEntity>();
            foreach (var id in ids)
            {
                var bo = await _context!.Set<TEntity>().FirstOrDefaultAsync(x => x.Id.Equals(id));
                if(bo == null)
                {
                    bos.Add(bo);
                }
            }
            try
            {
                _context!.Set<TEntity>().RemoveRange(bos);
                await _context.SaveChangesAsync();
                return new MessageResponse("删除成功!");
            }
            catch
            {
                throw new CustomException("删除失败!");
            }
        }
        public async Task<MessageResponse> SoftDeleteRangeByIdAsync(List<T> ids)
        {
            var bos = new List<TEntity>();
            foreach (var id in ids)
            {
                var bo = await _context!.Set<TEntity>().FirstOrDefaultAsync(x => x.Id.Equals(id));
                if (bo == null)
                {
                    bo.SoftDeleted();
                    bos.Add(bo);
                }
            }
            try
            {
                _context!.Set<TEntity>().UpdateRange(bos);
                await _context.SaveChangesAsync();
                return new MessageResponse("删除成功!");
            }
            catch
            {
                throw new CustomException("删除失败!");
            }

        }

        public async Task<MessageResponse> AddTOherAsync<Tid, TOher>(TOher oher)
            where TOher : class, IEntity<Tid>, new()
        {
            try
            {
                await _context.Set<TOher>().AddAsync(oher);
                await _context.SaveChangesAsync();
                return new MessageResponse("新增其他类型成功!");
            }
            catch(Exception ex) 
            {

                    throw new CustomException("添加其他类型失败!");
            }
        }


        public async Task<MessageResponse> UpdateOtherAsync<Tid, Other>(Other oher)
            where Other : class, IEntity<Tid>, new()
         {
            try
            {
                if (_context.ChangeTracker.Entries<Other>().Any(x => x.Entity.Id.Equals(oher.Id)))
                {
                    var trackingBo = _context.ChangeTracker.Entries<Other>().AsQueryable().Where(x => x.Entity.Id.Equals(oher.Id)).FirstOrDefault();
                    trackingBo.State = EntityState.Detached;
                }
                _context.Set<Other>().Update(oher);
                await _context.SaveChangesAsync();
                return new MessageResponse("更新其他类型成功!");
            }
            catch (Exception ex)
            {

                throw new CustomException("更新其他类型失败!");
            }
        }
        public async Task<MessageResponse> AddOtherAsync<Tid,Other>(Other oher)
            where Other : class, IEntity<Tid>, new()
        {
            try
            {
                await _context.Set<Other>().AddAsync(oher);
                await _context.SaveChangesAsync();
                return new MessageResponse("新增其他类型成功!");
            }
            catch (Exception ex)
            {

                throw new CustomException("新增其他类型失败!");
            }
        }
        public async Task<DataResponse<Other>> GetOtherAsync<Tid, Other>(Expression<Func<Other, bool>> predicate) where Other : class, IEntity<Tid>, new()
        {
            IQueryable<Other> dbSet = _context!.Set<Other>();
            var includePropertyExpressionCollection = ModelRelation.GetIncludeExpression<Tid, Other>();
            if (includePropertyExpressionCollection != null)
            {
                foreach (var includePropertyExpression in includePropertyExpressionCollection)
                {
                    dbSet = dbSet.Include(includePropertyExpression);
                }
            }
            var result = await dbSet.FirstOrDefaultAsync(predicate);
            return new DataResponse<Other>(result);
        }
    }
}
